package unimelb.filesync.common.protocol;

import java.util.concurrent.BlockingQueue;

import unimelb.filesync.common.message.FileSyncRequest;
import unimelb.filesync.common.message.NetworkMessage;
import unimelb.filesync.common.message.SequenceCompleteNotification;
import unimelb.filesync.common.misc.ProtocolContext;
import unimelb.filesync.common.network.NetworkConnection;
import unimelb.filesync.common.network.NetworkService;
import filesync.CopyBlockInstruction;
import filesync.NewBlockInstruction;

public class HandleNewBlockRequestTask implements Runnable {

    private final NetworkConnection hotLine;

    private final NetworkService networkService;

    private final BlockingQueue<FileSyncRequest> sentMsgQueue;

    public HandleNewBlockRequestTask(ProtocolContext context) {
        super();
        this.hotLine = context.getObject(ProtocolContext.HOT_LINE_CONNECTION);
        this.networkService = new NetworkService();
        this.sentMsgQueue = context.getObject(ProtocolContext.SENT_MSG_QUEUE);
    }

    @Override
    public void run() {
        while (true) {
            final NetworkMessage msg = networkService.readMessage(hotLine);
            if (msg == null) {
                System.exit(0);
            }
            
            if (NetworkMessage.TYPE_FILE_SYNC_REQUEST.equals(msg.getType())) {
                final FileSyncRequest newBlockRequest = msg.getContent();
                boolean found = false;
                while (!found) {
                    try {
                        final FileSyncRequest matchingMsg = sentMsgQueue.take();
                        if (matchingMsg.getSequenceId() == newBlockRequest.getSequenceId()
                                && matchingMsg.getInstructionId() == newBlockRequest.getInstructionId()) {
                            final NewBlockInstruction newBlockInstruction = new NewBlockInstruction((CopyBlockInstruction) matchingMsg.getInstruction());
                            final FileSyncRequest newBlockResponse = new FileSyncRequest(newBlockInstruction, matchingMsg.getSequenceId(),
                                    matchingMsg.getInstructionId());
                            networkService.writeMessage(NetworkMessage.createFileSyncRequestMsg(newBlockResponse), hotLine);
                            found = true;
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            } else if (NetworkMessage.TYPE_SEQUENCE_COMPLETE.equals(msg.getType())) {
                final SequenceCompleteNotification seqComplete = msg.getContent();

                boolean found = false;
                do {
                    final FileSyncRequest head = sentMsgQueue.peek();
                    if (head == null) {
                        found = true;
                    } else if (head.getSequenceId() <= seqComplete.getSequenceId()) {
                        try {
                            sentMsgQueue.take();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        found = true;
                    }
                } while (!found);
            }
        }
    }
}
